
#ifndef __APR_BUCKET_BRIGADE_H
#define __APR_BUCKET_BRIGADE_H

#include <apr.h>
#include <apr_buckets.h>
#include "../iface/debug.h"
#include "APRRWMutex.h"
#include "APRMutex.h"

/** A wrapper class around the 'bucket brigade' concept from the
 * Apache Portable Runtime. This class creates a FIFO pipe that can be
 * used in a variety of situations, including for encryption, file
 * operations, sockets and some types of data rearrangement. This is
 * because data from different sources can be expressed simply by
 * inserting by reference into the stream.  A more thorough
 * explanation of the bucket brigade concept can be found in the
 * Apache Portable Runtime documentation.
 */
class APRBucketBrigade
{
   public:
      APRBucketBrigade(apr_pool_t *imp);
      ~APRBucketBrigade();
      void addSocketBucket(apr_socket_t *tsock);
      void writeEOS();
      apr_size_t writeFileToStream(const char *filename, apr_size_t start, apr_size_t end, int isbinary);
      apr_size_t writefToStream(const char *format, ...) PRINTF_LIKE(2, 3);
      apr_size_t writeToStream(const char *data, apr_size_t numbytes);
      apr_size_t readFromStream(char *data, apr_size_t numbytes);
      void clear();
      apr_size_t removeFromStream(apr_size_t numbytes);
      apr_size_t popFromStream(char *data, int numbytes);
      apr_size_t popFromStreamFull(char *buffer, apr_size_t length, APRMutex *waitinglock);
      /** Has the end of the stream been reached?
       * 
       * @return Non-zero if the end-of-stream marker has been
       * reached. Zero otherwise. */
      int isClosed()
         { return (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bucketbrgde))); }
      /** How much space is left that can be written to?
       * 
       * @return Maximum amount of data in bytes that can be suffixed
       * to this stream. */
      apr_size_t packedSpaceRemaining()
         { return ((isClosed()) ? 0 : (apr_size_t)-1); }
      /** How much data is in this stream?
       * 
       * @return Amount of data in bytes in this stream. */
      apr_size_t realLength()
      {
         apr_status_t status;
         apr_off_t offt;
         status = apr_brigade_length(bucketbrgde, 0, &offt);
         CheckAPRError(status);
         return (apr_size_t)offt;
      }
      /** Does this bucket brigade have data?
       * 
       * @return Non-zero if there is data that can be read in the
       * stream, zero if there is no data or the stream has ended. */
      int hasData()
      {
         return (!((APR_BRIGADE_EMPTY(bucketbrgde)) ||
                  (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bucketbrgde)))));
      }
      int bufferToBucketBrigade(const char *buffer, size_t *length, size_t smallblock);
      int bucketBrigadeToBuffer(char **buffer, size_t *length);
      /** A read/write lock that ensures that the bucket brigade can
       * be used as a pipe between threads. */
      APRRWMutex *rwlock;
   private:
      APRBucketBrigade(APRBucketBrigade &o) {}
      APRBucketBrigade &operator=(APRBucketBrigade &o) { return *this; }
      /** Get the raw APR bucket allocator. */
      apr_bucket_alloc_t *directlyGetBucketAllocator() { return bucketalloc; }
      /** Get the raw APR bucket brigade. */
      apr_bucket_brigade *directlyGetBucketBrigade() { return bucketbrgde; }
      /** Add the size of this bucket to the total apparent size of
       * the bucket brigade data. */
      void addBucketSize(apr_bucket *bucket) { csize += bucket->length; }
      /** The parent memory pool of the bucket brigade class. */
      apr_pool_t *parent_mp;
      /** The memory pool used by the bucket brigade class. */
      apr_pool_t *mp;
      /** The bucket allocator used by the bucket brigade class. */
      apr_bucket_alloc_t *bucketalloc;
      /** The bucket brigade type from the APR library. */
      apr_bucket_brigade *bucketbrgde;
      /** The total size of all of the length-ascertainable parts of
       * the bucket brigade. */
      apr_size_t csize;
};

#endif
